/**
 * Copyright (C) 2007 The AsyncMail Group <asyncmail@googlegroups.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.asyncmail.util.streamscanner;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import org.apache.commons.logging.Log;

/**
 * This class used the clamd daemon to scant the given inputStream for viruses.
 */
public class ClamdStreamScanner implements StreamScanner {
    private String host;

    private int port;

    private Log logger;

    private final static String STREAM_CMD = "STREAM";

    private final static String STREAM_PORT_STRING = "PORT";

    private static final String FOUND_STRING = "FOUND";

    public void setLogger(Log logger) {
        this.logger = logger;
    }

    public ClamdStreamScanner(String host, int port) {
        this.port = port;
        this.host = host;
    }

    /**
     * @see com.googlecode.asyncmail.util.streamscanner.StreamScanner#scan(java.io.InputStream)
     */
    public String scan(InputStream in) {
        Socket clamdSocket = null;
        Socket cSocket = null;
        BufferedReader input = null;
        PrintWriter output = null;
        BufferedOutputStream clamdWriteStream = null;
        BufferedReader clamdResponseReader = null;

        int clamdPort = -1;
        try {
            clamdSocket = new Socket(host, port);
            input = new BufferedReader(new InputStreamReader(clamdSocket
                    .getInputStream(), "ASCII"));
            output = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                    clamdSocket.getOutputStream())), true);
            output.println(STREAM_CMD);
            output.flush();
            output.println(STREAM_PORT_STRING);
            output.flush();

            String rawPort = input.readLine();

            if (rawPort != null) {
                clamdPort = Integer.parseInt(rawPort
                        .substring(STREAM_PORT_STRING.length() + 1));
                cSocket = new Socket(clamdSocket.getInetAddress(), clamdPort);
                clamdWriteStream = new BufferedOutputStream(cSocket
                        .getOutputStream());
                int b = -1;

                while ((b = in.read()) != -1) {
                    clamdWriteStream.write(b);
                }
                clamdWriteStream.flush();
                clamdWriteStream.close();

                String response = input.readLine();
                if (response != null) {

                    if (response.endsWith(FOUND_STRING)) {
                        String resp = response.substring("Stream ".length());
                        logger.debug("Virus " + resp);
                        return resp;
                    } else {
                        logger.debug("Message clean");
                    }
                }
            }
        } catch (IOException e) {
            logger.error("Error communicating with clamd: " + e);
        } catch (NumberFormatException e2) {
            // Should not happen
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
                if (output != null) {
                    output.close();
                }
                if (clamdWriteStream != null) {
                    clamdWriteStream.close();
                }
                if (clamdResponseReader != null) {
                    clamdResponseReader.close();
                }
                if (clamdSocket != null) {
                    clamdSocket.close();
                }
                if (cSocket != null) {
                    cSocket.close();
                }
            } catch (IOException e) {
                // ignore in finally block
            }
        }
        return null;
    }
}
